#include <include/types.h>
#include <include/stdio.h>
#include <include/string.h>
#include <include/x86.h>

static int xpos;
static int ypos;
static uint16_t color=7;
static uint16_t *video;

static void itoa (char *buf, int base, int d);
static void putchar (int c);
static void scroll(void);
static void set_csr(void);

/* set the display positions. */
void
set_pos(int x,int y)
{
	xpos=x;
	ypos=y;
}
/* Clear the screen and initialize VIDEO, XPOS and YPOS.  */
void
cls(void)
{
	int i;
	uint16_t blank=' '|color<<8;
	for(i=0;i<25;i++) {
		memsetw(video+i*80,blank,80);
	}
	xpos=0;
	ypos=0;
	set_csr();
}
void
init_video(void)
{
	video=(uint16_t *)0xB8000;
	cls();
}

static void
itoa(char *buf,int base,int d)
{
	char *p=buf;
	char *p1,*p2;
	unsigned long ud=d;
	int divisor=10;

	/* If %d is specified and D is minus, put `-' in the head.  */
	if ('d'==base && d<0) {
		*p++='-';
		buf++;
		ud=-d;
	}
	else if ('x'==base) {
		divisor=16;
	}
	else if ('o'==base) {
		divisor=8;
	}

	/* Divide UD by DIVISOR until UD == 0.  */
	do {
		int remainder=ud%divisor;
		*p++ = (remainder<10)?remainder+'0':remainder+'a'-10;
	}while(ud/=divisor);

	/* Terminate BUF.  */
	*p=0;

	/* Reverse BUF.  */
	p1=buf;
	p2=p-1;
	while(p1<p2) {
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2--;
	}
}

/* scroll the sreen */
static void
scroll(void)
{	
	uint16_t blank,t;
	blank=' '|color<<8;

	if (ypos>24) {
		t=ypos-24;
		memcpy((uint8_t *)0xB8000,(uint8_t *)video+t*80*2,(25-t)*80*2);
		memsetw(video+(25-t)*80,blank,80);
		ypos=24;
	}
}
/* update the hardware cursor: the little blinking line
 * one the screen under the last character pressed!
 */
static void
set_csr(void)
{
	uint32_t t=ypos*80+xpos;

	outb(0x3D4,14);
	outb(0x3D5,t>>8);
	outb(0x3D4,15);
	outb(0x3D5,t);
}
/* Put the character C on the screen.  */
static void
putchar(int c)
{
  if (c=='\n') {
	  xpos=0;
      ypos++;
  }
  else if (c=='\r') {
	  xpos=0;
  }
  else if(c>=' ') {
	  *(video+(xpos+ypos*80))=c|(color<<8);
	  xpos++;
  }
  if (xpos>=80) {
	xpos=0;
	ypos++;
  }
  scroll();
  set_csr();
}

/* Format a string and print it on the screen, just like the libc
   function printf.  */
void
printk(const char *format, ...)
{
  char **arg=(char **) &format;
  int c;
  char buf[20];

  arg++;
  
  while ((c=*format++)!=0) {
      if (c!='%') {
		  putchar(c);
	  }
      else {
	  char *p;
	  c=*format++;
	  switch (c) {
	    case 'd':
	    case 'o':
	    case 'x':
	      itoa(buf, c, *((int *)arg++));
	      p=buf;
	      goto string;
	      break;
		case 'c':
		  putchar(*((int *)arg++));
		  break;
	    case 's':
	      p=*arg++;
	      if (!p)p="(null)";

	    string:
	      while (*p)
			  putchar (*p++);
	      break;

	    default:
	      putchar(*((int *) arg++));
	      break;
	  }
	  }
  }
}

void
set_color(uint8_t b,uint8_t f,uint16_t t)
{
	color=MAKE_COLOR(b,f,t);
}
